查看原文
其他

使用unicorn对ollvm字符串进行解密

新萌 看雪学苑 2022-07-01
本文为看雪论坛优秀文章
看雪论坛作者ID:新萌


题目出自3w班12月第一题——对抗字符串混淆。


样本是一个比较初级的ollvm混淆,其字符串解密过程在init_array中,所以so文件正常加载到内存中后,其加密的字符串就已经解密后写回原始地址了。

所以我们可以通过unicorn加载so文件,手动将解密后的字符串写回so文件,经过简单的patch就可以静态分析了。

本文参考了leadroyal与smartdone大佬的文章。

使用unicorn对字符串进行解密。


问题一:

偷懒使用unicorn直接将整个so文件load进memory,读取data的数据全是00,修复后也是错误的,我按照leadroyal大佬的脚本修改后是正确的。

问题二:

有时候使用unicorn map后,代码看着没有问题,但是跑起来就报错,可能你需要看看你分配的内存是不是不是1024的倍数。

脚本如下:
import binasciiimport math
import ida_bytesimport idaapifrom capstone import *from elftools.elf.elffile import ELFFilefrom elftools.elf.sections import SymbolTableSectionfrom keystone import *from unicorn import *from unicorn.arm_const import *

class FILFINIT(object): def __init__(self, file, mode): self.file = file self.fd = self.file_init(mode) self.elf = ELFFile(self.fd) self.file_size = self.file_size() self.data_divs = self.get_data_div()
def file_init(self, mode='rb+'): fd = open(self.file, mode=mode) return fd
def file_close(self): self.fd.close()
def file_seek_to(self, seekto): self.fd.seek(seekto, 0)
def file_read(self, size=-1): binary = self.fd.read(size) return binary
def file_write(self, data): self.fd.write(data)
def file_size(self): import os return os.path.getsize(self.file)
def get_data_div(self): data_divs = [] if self.elf: symtab = self.elf.get_section_by_name('.dynsym') assert isinstance(symtab, SymbolTableSection)
for sym in symtab.iter_symbols(): # sym.name:str if sym.name.startswith('.datadiv'): data_divs.append(sym.entry.st_value) return data_divs

class UCINIT(object): def __init__(self, file, mode, offset=0x10000, stack_size=0x10000, data_size=0x10000): self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB) self.fileinfo = FILFINIT(file, mode) self.data_divs = self.uc_filter() self.func_start_and_end = self.get_func_start_and_end()
self.uc = unicorn.Uc(UC_ARCH_ARM, UC_MODE_THUMB) self.image_base = 0x10000 self.offset = offset self.stack_size = stack_size self.data_size = data_size self.image_size = (math.ceil(self.fileinfo.file_size / self.offset) + 1) * self.offset self.uc.mem_map(self.image_base, self.image_size) self.init_seg() # self.uc.mem_write(self.image_base, self.fileinfo.file_read()) # 全文件导入是错误的,不知道为什么 self.stack_base = self.image_base + self.image_size + self.offset self.stack_top = self.stack_base + self.stack_size - 0x1000 self.uc.mem_map(self.stack_base, self.stack_size) self.uc.reg_write(UC_ARM_REG_SP, self.stack_top) self.data_base = self.stack_base + self.stack_size + self.offset self.uc.mem_map(self.data_base, self.data_size)
def init_seg(self): segs = [seg for seg in self.fileinfo.elf.iter_segments() if seg.header.p_type == 'PT_LOAD'] for seg in segs: self.uc.mem_write(self.image_base + seg.header.p_vaddr, seg.data())
def init_reg(self, regs): index = 0 for reg in regs: reg_num = 66 + index self.uc.reg_write(reg_num, reg)
def uc_run(self, target, end): targ = target + self.image_base util = self.image_base + end self.uc.emu_start(targ, util)
def uc_stop(self): self.uc.emu_stop() self.fileinfo.file_close()
def register_hook_code(self, htype, callback): self.uc.hook_add(htype, callback)
def uc_filter(self, size=0x2): data_divs = [] for data_div in self.fileinfo.data_divs: self.fileinfo.file_seek_to(data_div - 1) data = self.fileinfo.file_read(size) for inis in self.cs.disasm(data, 0): if inis.mnemonic == 'bx' and inis.op_str == 'lr': continue else: data_divs.append(data_div) self.fileinfo.file_seek_to(0) return data_divs
def get_func_start_and_end(self): data_divs = [] for data_div in self.data_divs: func = idaapi.get_func(data_div) func_start_and_end = (data_div, func.end_ea - 0x2) data_divs.append(func_start_and_end) return data_divs
def get_elf_data(self): data_section_header = self.fileinfo.elf.get_section_by_name('.data').header print('[+] .data addr {}-{}'.format(hex(data_section_header.sh_addr), hex(data_section_header.sh_size))) new_data = self.uc.mem_read(self.image_base + data_section_header.sh_addr, data_section_header.sh_size) print('[+] .data binary {}'.format(binascii.hexlify(new_data))) return new_data
def patch_so_data(self): data_section_header = self.fileinfo.elf.get_section_by_name('.data').header data_addr = data_section_header.sh_addr
# self.fileinfo.file_seek_to(data_addr) # self.fileinfo.file_write(self.get_elf_data()) new_data = self.get_elf_data() # new_data: bytearray print('[+] patch so data addr {}\n {}'.format(hex(data_addr), bytes(new_data))) ida_bytes.patch_bytes(data_addr, bytes(new_data))
def patch_data_div(self): ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB) for data_div in self.data_divs: binary = ks.asm('bx lr') print(bytes(binary[0])) # print(struct.pack('B', binary[0])) print('[+] patch so text .datadiv {}\n {}'.format(hex(data_div - 1), bytes(binary[0]))) ida_bytes.patch_bytes(data_div - 1, bytes(binary[0]))

def hook_code(uc: unicorn.Uc, address, size, user_data): cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB) me = uc.mem_read(address, size) for code in cs.disasm(me, size): print("[+] Tracing instructions at 0x%x, instructions size = ix%x, inst: %s %s" % ( address, size, code.mnemonic, code.op_str))

def hook_mem_access(uc, access, address, size, value, user_data): if access == UC_MEM_WRITE: print('[+] Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x' % (address, size, value)) else: # READ me = uc.mem_read(address, size) print('[+] Memory is being READ at 0x%x, data size = %u, data value = 0x%s' % ( address, size, binascii.hexlify(me)))

if __name__ == '__main__': print('[+] So file parse starting!') so = UCINIT('libcrack.so', 'rb') # so.get_elf_data() for start, end in so.func_start_and_end: print('[+] Data div addr {}-{}'.format(hex(start), hex(end))) so.init_reg((0, 0, 0)) # so.register_hook_code(UC_HOOK_CODE, hook_code) # so.register_hook_code(UC_HOOK_MEM_READ, hook_mem_access) # so.register_hook_code(UC_HOOK_MEM_WRITE, hook_mem_access) so.uc_run(start, end) so.patch_data_div() # so.patch_so_data() # so.get_elf_data() so.uc_stop() print('[+] So file run finished!')

uemu对字符串进行解密" class="anchor" href="#使用uemu对字符串进行解密">使用uEmu对字符串进行解密


# 下载uEmu$ git clonehttps://github.com/alexhude/uEmu
复制uEmu.py到IDA_Pro_7.5\plugins\下,重启ida。

找到函数,设置起始与结束断点:


在需要执行的汇编指令起点,右键-uEmu-start。


配置寄存器


ctrl+shift+alt+s单步运行。


如果你需要多行执行,单击要执行到某一行,右键-uEmu-run。


会自动向下执行到你单击的那一行。

查看内存,比如我们要查看0x1F0AD处的内存,看一下解密后的字符串内容:右键-uEmu-show memory range。


填写地址与长度,注意长度为10进制,选择add。

 



 


看雪ID:新萌

https://bbs.pediy.com/user-home-811222.htm

  *本文由看雪论坛 新萌 原创,转载请注明来自看雪社区



《安卓高级研修班》2021年秋季班火热招生中!



# 往期推荐





公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存